// =======================================================================================
/*!
 * @file    ArrangeVistaSidebar.c
 *
 * @brief   Arrange gadgets in a Windows Vista Sidebar.
 *
 * This program arranges the gadgets in the Windows Vista Sidebar. It was built as a
 * workaround for a bug that puts gadgets in a random order, apparently after installing
 * SP2.
 *
 * @author  Jorge Ramos (jramos at pobox dot com).
 *
 * @version 1.0.0.0 - Apr 10, 2011
 *          @li Initial version.
 *
 * @version 1.1.0.0 - Jul 24, 2011
 *          @li New parameters /q (quiet) and /i (install).
 *          @li Adaptive timeout.
 *
 * @version 1.2.0.0 - Aug 02, 2011
 *          @li Check out-of-screen gadgets before exit.
 *
 * @version 1.2.1.0 - Aug 05, 2011
 *          @li Change out-of-screen gadgets handling.
 *
 * @version 1.2.2.0 - Aug 07, 2011
 *          @li More changes in out-of-screen handling.
 *          @li Added option /c.
 *          @li Added VerInfo module.
 *
 * @version 1.2.2.1 - Aug 07, 2011
 *          @li Fixed bug in return value of AllOriginalGadgetsAreRestored.
 *          @li Changed MySleep logic.
 *
 * @remarks Copyright (c) 2011 Jorge Ramos.
 *
 * @remarks This software is provided 'as-is', without any express or implied warranty.
 *          Use it at your own risk. In no event shall the authors or copyright holders
 *          be held liable for any damages arising from the use of this software. Use
 *          and redistribution in source and binary forms, with or without modification,
 *          are permitted. Altered source versions must be plainly marked as such.
 *
 * @remarks This software is distributed in the hope that it will be useful, but WITHOUT
 *          ANY WARRANTY.
 */
// =======================================================================================

#include "Defs.h"
#include "Util.h"
#include "Help.h"
#include "VerInfo.h"

/*
 * Stub module that fakes multiple monitor apis on Win32 OSes without them.
 */

#pragma warning (disable: 4706)         /* assignment within conditional expression */

#define COMPILE_MULTIMON_STUBS

#include <multimon.h>

#pragma warning (default: 4706)         /* assignment within conditional expression */

//////////////////////////////////////////////////////////////////////////////////////////

const TCHAR szAppBarWindowClass   [] = _T ("Sidebar_AppBarWindow");
const TCHAR szAppBarBulletClass   [] = _T ("Sidebar_AppBarBullet");
const TCHAR szBasicWindowClass    [] = _T ("BasicWindow");
const TCHAR szHTMLHostWindowClass [] = _T ("Sidebar_HTMLHostWindow");
const TCHAR szIEServerWindowClass [] = _T ("Internet Explorer_Server");
const TCHAR szConfigFolderName    [] = _T ("ArrangeVistaSidebar");
const TCHAR szConfigBaseName      [] = _T ("ArrangeVistaSidebar.ini");

//////////////////////////////////////////////////////////////////////////////////////////

#define SIDEBAR_MAX             16
#define SIDEBAR_DOCKSIDE_LEFT   1
#define SIDEBAR_DOCKSIDE_RIGHT  2
#define NO_GADGET               UINT_MAX

struct SIDEBAR_T
{
    HWND hwndSidebarWindow;
    RECT rcSidebarWindow;
    UINT nSidebarDockSide;
    BOOL bErrorLoadingGadgets;
    // Following items are indexed from 0 to nGadgets - 1.
    UINT nGadgets;
    TCHAR szSidebarWindowTitle [256];
    HWND hwndGadgetWindow [SIDEBAR_MAX];
    HWND hwndHTMLHostWindow [SIDEBAR_MAX];
    HWND hwndIEServerWindow [SIDEBAR_MAX];
    BOOL bIsOutOfScreen [SIDEBAR_MAX];
    BOOL bOriginalPositionNotFound [SIDEBAR_MAX];
    RECT rcGadgetWindow [SIDEBAR_MAX];
    RECT rcGadgetWindowOriginal [SIDEBAR_MAX];
    RECT rcHTMLHostWindow [SIDEBAR_MAX];
    TCHAR szGadgetWindowTitle [SIDEBAR_MAX][256];
    UINT nGadgetOrder [SIDEBAR_MAX];
    UINT nGadgetOrderOriginal [SIDEBAR_MAX];
    // Following items are indexed from 0 to nOriginalGadgets - 1.
    UINT nOriginalGadgets;
    TCHAR szOriginalGadgetWindowTitle [SIDEBAR_MAX][256];
};

typedef struct SIDEBAR_T SIDEBAR;

struct PARAM_T
{
    UINT nMethod;
    BOOL bShowWindows;
    BOOL bSaveGadgetsPosition;
    BOOL bRestoreGadgetsPosition;
    UINT nTimeout;
    UINT nTraceOutputFlag;
    BOOL bQuiet;
    BOOL bInstall;
    BOOL bRestartTimerIfCursorMoves;
};

typedef struct PARAM_T PARAM;

//////////////////////////////////////////////////////////////////////////////////////////

LPTSTR _tcsdup2 (LPCTSTR pszSource)
{
    LPTSTR pszTemp = _tcsdup (pszSource);

    if (pszTemp == NULL)
    {
        DisplayWin32Message (ERROR_NOT_ENOUGH_MEMORY, _T ("_tcsdup"));
        exit (3);
    }

    return pszTemp;
}

//////////////////////////////////////////////////////////////////////////////////////////

VOID GetConfigFileName (LPTSTR pszConfigFileName, size_t nCount)
{
    DWORD dwChars;
    INT nChars;
    TCHAR szLocalAppData [MAX_PATH];

    dwChars = GetEnvironmentVariable (_T ("LOCALAPPDATA"), szLocalAppData, _countof (szLocalAppData));

    if (dwChars == 0)
    {
        DisplayError (_T ("Environment variable LOCALAPPDATA not found\n"));
        exit (3);
    }

    if (dwChars >= _countof (szLocalAppData))
    {
        DisplayWin32Message (ERROR_INSUFFICIENT_BUFFER, _T ("GetEnvironmentVariable"));
        exit (3);
    }

    if (szLocalAppData [dwChars - 1] == _T ('\\'))
    {
        szLocalAppData [dwChars - 1] = _T ('\0');
    }

    nChars = _sntprintf2 (pszConfigFileName, nCount, _T ("%s\\%s\\%s"), szLocalAppData, szConfigFolderName, szConfigBaseName);

    if (nChars < 0)
    {
        DisplayWin32Message (ERROR_INSUFFICIENT_BUFFER, _T ("_sntprintf2"));
        exit (3);
    }
}

//////////////////////////////////////////////////////////////////////////////////////////

BOOL SaveCursorPos (LPPOINT ptCursor)
{
    if (Win32Call (GetCursorPos (ptCursor), _T ("SaveCursorPos.GetCursorPos")))
    {
        return TRUE;
    }

    ptCursor->x = INT_MAX;
    ptCursor->y = INT_MAX;

    return FALSE;
}

//////////////////////////////////////////////////////////////////////////////////////////

VOID RestoreCursorPos (LPPOINT ptCursor)
{
    if (ptCursor->x != INT_MAX && ptCursor->y != INT_MAX)
    {
        (VOID) Win32Call (SetCursorPos (ptCursor->x, ptCursor->y), _T ("RestoreCursorPos.SetCursorPos"));
    }
}

//////////////////////////////////////////////////////////////////////////////////////////

BOOL IsButtonDown (VOID)
{
    UINT nButton = 0;

    if ((GetAsyncKeyState (VK_LBUTTON) & 0x8000) != 0) nButton |= 0x0001;
    if ((GetAsyncKeyState (VK_RBUTTON) & 0x8000) != 0) nButton |= 0x0002;
    if ((GetAsyncKeyState (VK_MBUTTON) & 0x8000) != 0) nButton |= 0x0004;
    if ((GetAsyncKeyState (VK_MENU   ) & 0x8000) != 0) nButton |= 0x0010;
    if ((GetAsyncKeyState (VK_SHIFT  ) & 0x8000) != 0) nButton |= 0x0020;
    if ((GetAsyncKeyState (VK_CONTROL) & 0x8000) != 0) nButton |= 0x0040;

    if (nButton != 0)
    {
        Trace (_T ("IsButtonDown: Button is down:%s%s%s%s%s%s\n"), (nButton & 0x0001) != 0 ? _T (" VK_LBUTTON") : _T (""),
                                                                   (nButton & 0x0002) != 0 ? _T (" VK_RBUTTON") : _T (""),
                                                                   (nButton & 0x0004) != 0 ? _T (" VK_MBUTTON") : _T (""),
                                                                   (nButton & 0x0010) != 0 ? _T (" VK_MENU") : _T (""),
                                                                   (nButton & 0x0010) != 0 ? _T (" VK_SHIFT") : _T (""),
                                                                   (nButton & 0x0010) != 0 ? _T (" VK_CONTROL") : _T (""));
        return TRUE;
    }

    return FALSE;
}

//////////////////////////////////////////////////////////////////////////////////////////

BOOL EqualPoint (POINT pt1, POINT pt2)
{
    return pt1.x == pt2.x && pt1.y == pt2.y;
}

//////////////////////////////////////////////////////////////////////////////////////////
// TRUE  - Wait OK.
// FALSE - Cursor was moved (only if bCheckCursor is TRUE).

BOOL MySleep (DWORD dwMilliseconds, BOOL bCheckCursor)
{
    BOOL bRetval = TRUE;
    POINT ptCursor1;
    POINT ptCursor2;

    SaveCursorPos (&ptCursor1);

    Trace (_T ("MySleep: Waiting %ums\n"), dwMilliseconds);

    Sleep (dwMilliseconds);

    if (bCheckCursor)
    {
        SaveCursorPos (&ptCursor2);

        if (! EqualPoint (ptCursor1, ptCursor2))
        {
            bRetval = FALSE;
        }
    }
#ifdef _DEBUG
    else
    {
        SaveCursorPos (&ptCursor2);

        if (! EqualPoint (ptCursor1, ptCursor2))
        {
            Trace (_T ("MySleep: Cursor movement ignored\n"));
        }
    }
#endif

    return bRetval;
}

//////////////////////////////////////////////////////////////////////////////////////////

BOOL GetClassName2 (HWND hWnd, LPTSTR pszClassName, size_t nCount)
{
    BOOL bRetval = FALSE;
    INT nChars;

    nChars = GetClassName (hWnd, pszClassName, (INT) nCount);

    if (nChars != 0)
    {
        if ((size_t) nChars < nCount - 1)
        {
            bRetval = TRUE;
        }
        else
        {
            SetLastError (ERROR_INSUFFICIENT_BUFFER);
        }
    }
    else
    {
        if (nCount > 0)
        {
            *pszClassName = _T ('\0');
        }
    }

    return bRetval;
}

//////////////////////////////////////////////////////////////////////////////////////////

BOOL GetWindowText2 (HWND hWnd, LPTSTR pszText, size_t nCount)
{
    BOOL bRetval = FALSE;
    INT nChars;

    SetLastError (NO_ERROR);

    nChars = GetWindowText (hWnd, pszText, (INT) nCount);

    if (nChars != 0)
    {
        if ((size_t) nChars < nCount - 1)
        {
            bRetval = TRUE;
        }
        else
        {
            _sntprintf2 (pszText, nCount, _T ("0x%08X"), hWnd);

            SetLastError (ERROR_INSUFFICIENT_BUFFER);
        }
    }
    else
    {
        if (GetLastError () == NO_ERROR)
        {
            bRetval = TRUE;
        }
        else
        {
            if (nCount > 0)
            {
                *pszText = _T ('\0');
            }
        }
    }

    return bRetval;
}

//////////////////////////////////////////////////////////////////////////////////////////

BOOL GetPrivateProfileString2 (LPCTSTR pszAppName, LPCTSTR pszKeyName, LPCTSTR pszDefault, LPTSTR pszReturnedString, DWORD nSize, LPCTSTR pszFileName)
{
    BOOL bRetval = FALSE;
    DWORD nChars;

    SetLastError (NO_ERROR);

    nChars = GetPrivateProfileString (pszAppName, pszKeyName, pszDefault, pszReturnedString, nSize, pszFileName);

    if (nChars > 0)
    {
        if (pszAppName != NULL && pszKeyName != NULL)
        {
            if (nChars < nSize - 1)
            {
                bRetval = TRUE;
            }
            else
            {
                SetLastError (ERROR_INSUFFICIENT_BUFFER);
            }
        }
        else
        {
            if (nChars < nSize - 2)
            {
                bRetval = TRUE;
            }
            else
            {
                SetLastError (ERROR_INSUFFICIENT_BUFFER);
            }
        }
    }
    else
    {
        if (GetLastError () == NO_ERROR)
        {
            SetLastError (ERROR_NOT_FOUND);
        }
    }

    return bRetval;
}

//////////////////////////////////////////////////////////////////////////////////////////

VOID DisplayHelp (VOID)
{
    static HELPMSG aHelpMsg [] =
    {
        {  0,  0, _T ("Arrange gadgets in a Windows Vista Sidebar.") },
        {  0, 14, _T ("Usage: ArrangeVistaSidebar [OPTION]...") },
        {  0, 28, _T ("Options:") },
        { 32, 28, _T ("-?") },
        { 56, 28, _T ("Display this help and exit.") },
        { 32, 36, _T ("-??") },
        { 56, 36, _T ("Output version information and exit.") },
        { 32, 44, _T ("-s") },
        { 56, 44, _T ("Save gadgets positions.") },
        { 32, 52, _T ("-r") },
        { 56, 52, _T ("Restore gadgets positions.") },
        { 32, 60, _T ("-t n") },
        { 56, 60, _T ("Maximum time in seconds to wait for Sidebar finish loading (default = 30s).") },
        { 32, 68, _T ("-q") },
        { 56, 68, _T ("Quiet mode (disable common error messages - used mostly by the install program).") },
        { 32, 76, _T ("-c") },
        { 56, 76, _T ("Restart timer if cursor moves.") },
        { 32, 84, _T ("-l") },
        { 56, 84, _T ("Output debug information to a log file.") },
#ifdef _UNICODE
        { 32, 92, _T ("-a") },
        { 56, 92, _T ("Use ANSI characters to write the log file.") },
        { 32,100, _T ("-u") },
        { 56,100, _T ("Use UNICODE characters to write the log file (default).") },
#else
        { 32, 92, _T ("-a") },
        { 56, 92, _T ("Use ANSI characters to write the log file (default).") },
        { 32,100, _T ("-u") },
        { 56,100, _T ("Use UNICODE characters to write the log file.") },
#endif
        { 32,108, _T ("-w") },
        { 56,108, _T ("Output Sidebar window structure and identify its windows on screen.") },
        { 32,116, _T ("-d") },
        { 56,116, _T ("Output debug information to the application debugger.") }
    };

    SetHelpBufferSize (4096); DisplayHelpDlg (aHelpMsg, _countof (aHelpMsg));
}

//////////////////////////////////////////////////////////////////////////////////////////

VOID DisplayVersion (VOID)
{
    DisplayHelpStd ();
}

//////////////////////////////////////////////////////////////////////////////////////////

VOID InvalidCommandLine (LPTSTR pszCmdLine)
{
    Trace (_T ("InvalidCommandLine: [%s]\n"), pszCmdLine);

    DisplayError (_T ("Unrecognized command line.\n\n")
                  _T ("Try \"%s --help\" for more information.\n"), GetVersionInfoInternalName ());
}

//////////////////////////////////////////////////////////////////////////////////////////

INT Initialize (HINSTANCE hInstance, LPTSTR pszCmdLine, PARAM* pParam)
{
    INT nCommands;
    INT nArg;
    BOOL bAnsi = FALSE;
    BOOL bUnicode = FALSE;

    ZeroMemory (pParam, sizeof (PARAM));

    pParam->nTimeout = 30;
    pParam->nTraceOutputFlag = TRACE_OUTPUT_NONE;

    TRACE (_T ("//////////////////////////////////////////////////////////////////////////////////////////\n"));

    SetApplicationInstance (hInstance);
    SetApplicationName (GetVersionInfoInternalName ());
    SetApplicationVersion (GetVersionInfoFileVersion ());
    SetApplicationAuthor (GetVersionInfoCompanyName ());
    SetApplicationNotice (GetVersionInfoComments ());

    for (nCommands = 0, nArg = 1; nArg < __argc; nArg++)
    {
        LPTSTR pArg = __targv [nArg];

        if (pArg [0] == _T ('-') || pArg [0] == _T ('/'))
        {
            if (*++pArg == _T ('-'))
            {
                pArg++;
            }

            if (_tcsicmp (pArg, _T ("?")) == 0 || _tcsicmp (pArg, _T ("help")) == 0)
            {
                DisplayHelp ();
                return 1;
            }

            if (_tcsicmp (pArg, _T ("??")) == 0 || _tcsicmp (pArg, _T ("version")) == 0)
            {
                DisplayVersion ();
                return 1;
            }

            if (_tcsicmp (pArg, _T ("a")) == 0 || _tcsicmp (pArg, _T ("ansi")) == 0)
            {
                pParam->nTraceOutputFlag |= TRACE_OUTPUT_FILE; bAnsi = TRUE;
            }
            else if (_tcsicmp (pArg, _T ("u")) == 0 || _tcsicmp (pArg, _T ("unicode")) == 0)
            {
                pParam->nTraceOutputFlag |= TRACE_OUTPUT_FILE; bUnicode = TRUE;
            }
            else if (_tcsicmp (pArg, _T ("d")) == 0 || _tcsicmp (pArg, _T ("debug")) == 0)
            {
                pParam->nTraceOutputFlag |= TRACE_OUTPUT_DEBUG;
            }
            else if (_tcsicmp (pArg, _T ("l")) == 0 || _tcsicmp (pArg, _T ("log")) == 0)
            {
                pParam->nTraceOutputFlag |= TRACE_OUTPUT_FILE;
            }
            else if (_tcsicmp (pArg, _T ("w")) == 0 || _tcsicmp (pArg, _T ("windows")) == 0)
            {
                pParam->bShowWindows = TRUE; nCommands++;
            }
            else if (_tcsicmp (pArg, _T ("s")) == 0 || _tcsicmp (pArg, _T ("save")) == 0)
            {
                pParam->bSaveGadgetsPosition = TRUE; nCommands++;
            }
            else if (_tcsicmp (pArg, _T ("r")) == 0 || _tcsicmp (pArg, _T ("restore")) == 0)
            {
                pParam->bRestoreGadgetsPosition = TRUE; nCommands++;
                pParam->nMethod = 3;
            }
            else if (_tcsicmp (pArg, _T ("r1")) == 0)
            {
                pParam->bRestoreGadgetsPosition = TRUE; nCommands++;
                pParam->nMethod = 1;
            }
            else if (_tcsicmp (pArg, _T ("r2")) == 0)
            {
                pParam->bRestoreGadgetsPosition = TRUE; nCommands++;
                pParam->nMethod = 2;
            }
            else if (_tcsicmp (pArg, _T ("r3")) == 0)
            {
                pParam->bRestoreGadgetsPosition = TRUE; nCommands++;
                pParam->nMethod = 3;
            }
            else if (_tcsicmp (pArg, _T ("t")) == 0 || _tcsicmp (pArg, _T ("timeout")) == 0)
            {
                if (++nArg >= __argc)
                {
                    InvalidCommandLine (pszCmdLine);
                    return 3;
                }

                pParam->nTimeout = _ttoi (__targv [nArg]);
            }
            else if (_tcsicmp (pArg, _T ("q")) == 0 || _tcsicmp (pArg, _T ("quiet")) == 0)
            {
                pParam->bQuiet = TRUE;
            }
            else if (_tcsicmp (pArg, _T ("i")) == 0 || _tcsicmp (pArg, _T ("install")) == 0)
            {
                pParam->bQuiet = pParam->bInstall = TRUE;
            }
            else if (_tcsicmp (pArg, _T ("c")) == 0)
            {
                pParam->bRestartTimerIfCursorMoves = TRUE;
            }
            else
            {
                InvalidCommandLine (pszCmdLine);
                return 3;
            }
        }
        else
        {
            InvalidCommandLine (pszCmdLine);
            return 3;
        }
    }

    if (nCommands > 1)
    {
        InvalidCommandLine (pszCmdLine);
        return 3;
    }

    if ((pParam->nTraceOutputFlag & TRACE_OUTPUT_FILE) != 0)
    {
        if (bAnsi)
        {
            if (bUnicode)
            {
                InvalidCommandLine (pszCmdLine);
                return 3;
            }

            pParam->nTraceOutputFlag |= TRACE_OUTPUT_ANSI;
        }
        else
        {
            if (! bUnicode)
            {
                if (sizeof (TCHAR) == 1)
                {
                    pParam->nTraceOutputFlag |= TRACE_OUTPUT_ANSI;
                }
            }
        }
    }

    SetTraceOutput (pParam->nTraceOutputFlag);

    Trace (_T ("%s %s\n"), GetApplicationName (), GetApplicationVersion ());

    return 0;
}

//////////////////////////////////////////////////////////////////////////////////////////

BOOL CALLBACK FindSidebarWindowCallback (HWND hWnd, LPARAM lParam)
{
    SIDEBAR* pSidebar = (SIDEBAR*) lParam;
    TCHAR szClassName [256];

    if (Win32Call (GetClassName2 (hWnd, szClassName, _countof (szClassName)), _T ("FindSidebarWindowCallback.GetClassName2")))
    {
        if (_tcsicmp (szClassName, szAppBarWindowClass) == 0)
        {
            pSidebar->hwndSidebarWindow = hWnd;
            return FALSE;
        }
    }

    return TRUE;
}

//////////////////////////////////////////////////////////////////////////////////////////

BOOL CALLBACK FindHTMLHostWindowCallback (HWND hWnd, LPARAM lParam)
{
    SIDEBAR* pSidebar = (SIDEBAR*) lParam;
    HWND hwndParent;
    TCHAR szClassName [256];

    hwndParent = (HWND) (LONG_PTR) GetWindowLong (hWnd, GWL_HWNDPARENT);

    if (hwndParent == pSidebar->hwndGadgetWindow [pSidebar->nGadgets])
    {
        if (Win32Call (GetClassName2 (hWnd, szClassName, _countof (szClassName)), _T ("FindHTMLHostWindowCallback.GetClassName2")))
        {
            if (_tcsicmp (szClassName, szHTMLHostWindowClass) == 0)
            {
                pSidebar->hwndHTMLHostWindow [pSidebar->nGadgets] = hWnd;
                return FALSE;
            }
        }
    }

    return TRUE;
}

//////////////////////////////////////////////////////////////////////////////////////////

BOOL CALLBACK FindIEServerWindowCallback (HWND hWnd, LPARAM lParam)
{
    SIDEBAR* pSidebar = (SIDEBAR*) lParam;
    TCHAR szClassName [256];

    if (Win32Call (GetClassName2 (hWnd, szClassName, _countof (szClassName)), _T ("FindIEServerWindowCallback.GetClassName2")))
    {
        if (_tcsicmp (szClassName, szIEServerWindowClass) == 0)
        {
            pSidebar->hwndIEServerWindow [pSidebar->nGadgets] = hWnd;
            return FALSE;
        }
    }

    return TRUE;
}

//////////////////////////////////////////////////////////////////////////////////////////

BOOL CALLBACK FindGadgetWindowCallback (HWND hWnd, LPARAM lParam)
{
    SIDEBAR* pSidebar = (SIDEBAR*) lParam;
    HWND hwndParent;
    TCHAR szClassName [256];
    TCHAR szTitle [256];

    hwndParent = (HWND) (LONG_PTR) GetWindowLong (hWnd, GWL_HWNDPARENT);

    if (hwndParent == pSidebar->hwndSidebarWindow)
    {
        if (Win32Call (GetClassName2 (hWnd, szClassName, _countof (szClassName)), _T ("FindGadgetWindowCallback.GetClassName2")))
        {
            if (_tcsicmp (szClassName, szBasicWindowClass) == 0)
            {
                if (pSidebar->nGadgets < _countof (pSidebar->hwndGadgetWindow))
                {
                    pSidebar->hwndGadgetWindow [pSidebar->nGadgets] = hWnd;

                    (VOID) EnumWindows (FindHTMLHostWindowCallback, (LPARAM) pSidebar);

                    if (pSidebar->hwndHTMLHostWindow [pSidebar->nGadgets] != NULL)
                    {
                        (VOID) EnumChildWindows (pSidebar->hwndHTMLHostWindow [pSidebar->nGadgets], FindIEServerWindowCallback, (LPARAM) pSidebar);

                        pSidebar->nGadgets++;
                    }
                    else
                    {
                        (VOID) GetWindowText2 (hWnd, szTitle, _countof (szTitle));
                        Trace (_T ("FindGadgetWindowCallback: HTMLHostWindow not found for gadget \"%s\"\n"), szTitle);
                        pSidebar->bErrorLoadingGadgets = TRUE;
                    }
                }
                else
                {
                    (VOID) GetWindowText2 (hWnd, szTitle, _countof (szTitle));
                    Trace (_T ("FindGadgetWindowCallback: Not enough space in array for gadget \"%s\"\n"), szTitle);
                    pSidebar->bErrorLoadingGadgets = TRUE;
                }
            }
        }
    }

    return TRUE;
}

//////////////////////////////////////////////////////////////////////////////////////////

VOID GetPrimaryScreenRect (RECT* pRect)
{
    static RECT rcPrimaryScreen = { 0, 0, 0, 0 };

    pRect->left = 0;
    pRect->top = 0;
    pRect->right = GetSystemMetrics (SM_CXSCREEN);
    pRect->bottom = GetSystemMetrics (SM_CYSCREEN);

    if (! EqualRect (pRect, &rcPrimaryScreen))
    {
        rcPrimaryScreen = *pRect;

        Trace (_T ("GetPrimaryScreenRect: Primary screen rectangle is (%d %d %d %d)\n"), rcPrimaryScreen.left, rcPrimaryScreen.top, rcPrimaryScreen.right, rcPrimaryScreen.bottom);
    }
}

//////////////////////////////////////////////////////////////////////////////////////////

VOID GetVirtualScreenRect (RECT* pRect)
{
    static RECT rcVirtualScreen = { 0, 0, 0, 0 };

    pRect->left = GetSystemMetrics (SM_XVIRTUALSCREEN);
    pRect->top = GetSystemMetrics (SM_YVIRTUALSCREEN);
    pRect->right = pRect->left + GetSystemMetrics (SM_CXVIRTUALSCREEN);
    pRect->bottom = pRect->top + GetSystemMetrics (SM_CYVIRTUALSCREEN);

    if (! EqualRect (pRect, &rcVirtualScreen))
    {
        rcVirtualScreen = *pRect;

        Trace (_T ("GetVirtualScreenRect: Virtual screen rectangle is (%d %d %d %d)\n"), rcVirtualScreen.left, rcVirtualScreen.top, rcVirtualScreen.right, rcVirtualScreen.bottom);
    }
}

//////////////////////////////////////////////////////////////////////////////////////////
// > 0 - Sidebar is loaded.
//   0 - Sidebar window not found.
// < 0 - Error loading Sidebar (must be between -1 and -10, see logic in LoadCurrentSidebar).

INT LoadSidebar (SIDEBAR* pSidebar)
{
    INT nRetval;
    RECT rcVirtualScreen;
    RECT rcIntersect;
    HMONITOR hMonitor;
    MONITORINFO mi;

    GetVirtualScreenRect (&rcVirtualScreen);

    ZeroMemory (pSidebar, sizeof (SIDEBAR));

    (VOID) EnumWindows (FindSidebarWindowCallback, (LPARAM) pSidebar);

    if (pSidebar->hwndSidebarWindow != NULL)
    {
        if (Win32Call (GetWindowText2 (pSidebar->hwndSidebarWindow, pSidebar->szSidebarWindowTitle, _countof (pSidebar->szSidebarWindowTitle)), _T ("LoadSidebar.GetWindowText2")))
        {
            if (IsWindowVisible (pSidebar->hwndSidebarWindow))
            {
                if (! IsIconic (pSidebar->hwndSidebarWindow))
                {
                    if (Win32Call (GetWindowRect (pSidebar->hwndSidebarWindow, &pSidebar->rcSidebarWindow), _T ("LoadSidebar.GetWindowRect")))
                    {
                        TRACE (_T ("Sidebar \"%s\" is visible at (%d %d %d %d)\n"), pSidebar->szSidebarWindowTitle, pSidebar->rcSidebarWindow.left, pSidebar->rcSidebarWindow.top, pSidebar->rcSidebarWindow.right, pSidebar->rcSidebarWindow.bottom);

                        if (IntersectRect (&rcIntersect, &rcVirtualScreen, &pSidebar->rcSidebarWindow))
                        {
                            hMonitor = MonitorFromWindow (pSidebar->hwndSidebarWindow, MONITOR_DEFAULTTONULL);

                            if (hMonitor != NULL)
                            {
                                mi.cbSize = sizeof (mi);

                                if (Win32Call (GetMonitorInfo (hMonitor, &mi), _T ("LoadSidebar.GetMonitorInfo")))
                                {
                                    TRACE (_T ("Work area of display monitor of sidebar \"%s\" is (%d %d %d %d)\n"), pSidebar->szSidebarWindowTitle, mi.rcWork.left, mi.rcWork.top, mi.rcWork.right, mi.rcWork.bottom);

                                    if (pSidebar->rcSidebarWindow.left <= mi.rcWork.left)
                                    {
                                        Trace (_T ("Sidebar \"%s\" is docked at left (%d %d %d %d)\n"), pSidebar->szSidebarWindowTitle, pSidebar->rcSidebarWindow.left, pSidebar->rcSidebarWindow.top, pSidebar->rcSidebarWindow.right, pSidebar->rcSidebarWindow.bottom);
                                        pSidebar->nSidebarDockSide = SIDEBAR_DOCKSIDE_LEFT;
                                        nRetval = 1;
                                    }
                                    else
                                    {
                                        if (pSidebar->rcSidebarWindow.right >= mi.rcWork.right)
                                        {
                                            Trace (_T ("Sidebar \"%s\" is docked at right (%d %d %d %d)\n"), pSidebar->szSidebarWindowTitle, pSidebar->rcSidebarWindow.left, pSidebar->rcSidebarWindow.top, pSidebar->rcSidebarWindow.right, pSidebar->rcSidebarWindow.bottom);
                                            pSidebar->nSidebarDockSide = SIDEBAR_DOCKSIDE_RIGHT;
                                            nRetval = 1;
                                        }
                                        else
                                        {
                                            Trace (_T ("LoadSidebar: Cannot determine sidebar \"%s\" dock side (%d %d %d %d)\n"), pSidebar->szSidebarWindowTitle, pSidebar->rcSidebarWindow.left, pSidebar->rcSidebarWindow.top, pSidebar->rcSidebarWindow.right, pSidebar->rcSidebarWindow.bottom);
                                            nRetval = -8;
                                        }
                                    }
                                }
                                else
                                {
                                    Trace (_T ("LoadSidebar: Cannot get monitor info of sidebar \"%s\" at (%d %d %d %d)\n"), pSidebar->szSidebarWindowTitle, pSidebar->rcSidebarWindow.left, pSidebar->rcSidebarWindow.top, pSidebar->rcSidebarWindow.right, pSidebar->rcSidebarWindow.bottom);
                                    nRetval = -7;
                                }
                            }
                            else
                            {
                                Trace (_T ("LoadSidebar: Cannot get monitor of sidebar \"%s\" at (%d %d %d %d)\n"), pSidebar->szSidebarWindowTitle, pSidebar->rcSidebarWindow.left, pSidebar->rcSidebarWindow.top, pSidebar->rcSidebarWindow.right, pSidebar->rcSidebarWindow.bottom);
                                nRetval = -6;
                            }
                        }
                        else
                        {
                            Trace (_T ("LoadSidebar: Sidebar \"%s\" is out of virtual screen (%d %d %d %d)\n"), pSidebar->szSidebarWindowTitle, pSidebar->rcSidebarWindow.left, pSidebar->rcSidebarWindow.top, pSidebar->rcSidebarWindow.right, pSidebar->rcSidebarWindow.bottom);
                            nRetval = -5;
                        }
                    }
                    else
                    {
                        Trace (_T ("LoadSidebar: Cannot get window rectangle of sidebar \"%s\"\n"), pSidebar->szSidebarWindowTitle);
                        nRetval = -4;
                    }
                }
                else
                {
                    Trace (_T ("LoadSidebar: Sidebar \"%s\" is minimized\n"), pSidebar->szSidebarWindowTitle);
                    nRetval = -3;
                }
            }
            else
            {
                Trace (_T ("LoadSidebar: Sidebar \"%s\" is not visible\n"), pSidebar->szSidebarWindowTitle);
                nRetval = -2;
            }
        }
        else
        {
            Trace (_T ("LoadSidebar: Cannot get window text of sidebar window 0x%08X\n"), pSidebar->hwndSidebarWindow);
            nRetval = -1;
        }
    }
    else
    {
        Trace (_T ("LoadSidebar: Sidebar window not found\n"));
        nRetval = 0;
    }

    return nRetval;
}

//////////////////////////////////////////////////////////////////////////////////////////
// > 0 - Gadget is loaded.
//   0 - Gadget window not found (internal consistency error).
// < 0 - Error loading gadget (must be between -1 and -10, see logic in LoadGadgets).

INT LoadGadget (SIDEBAR* pSidebar, UINT nGadget)
{
    INT nRetval;
    RECT rcVirtualScreen;
    RECT rcIntersect;

    GetVirtualScreenRect (&rcVirtualScreen);

    if (pSidebar->hwndGadgetWindow [nGadget] != NULL)
    {
        if (Win32Call (GetWindowText2 (pSidebar->hwndGadgetWindow [nGadget], pSidebar->szGadgetWindowTitle [nGadget], _countof (pSidebar->szGadgetWindowTitle [nGadget])), _T ("LoadGadget.GetWindowText2")))
        {
            if (IsWindowVisible (pSidebar->hwndGadgetWindow [nGadget]))
            {
                if (! IsIconic (pSidebar->hwndGadgetWindow [nGadget]))
                {
                    if (Win32Call (GetWindowRect (pSidebar->hwndGadgetWindow [nGadget], &pSidebar->rcGadgetWindow [nGadget]), _T ("LoadGadget.GetWindowRect")))
                    {
                        TRACE (_T ("Gadget \"%s\" is loaded at (%d %d %d %d)\n"), pSidebar->szGadgetWindowTitle [nGadget], pSidebar->rcGadgetWindow [nGadget].left, pSidebar->rcGadgetWindow [nGadget].top, pSidebar->rcGadgetWindow [nGadget].right, pSidebar->rcGadgetWindow [nGadget].bottom);

                        if (Win32Call (GetWindowRect (pSidebar->hwndHTMLHostWindow [nGadget], &pSidebar->rcHTMLHostWindow [nGadget]), _T ("LoadGadget.GetWindowRect")))
                        {
                            if (IntersectRect (&rcIntersect, &rcVirtualScreen, &pSidebar->rcGadgetWindow [nGadget]) && EqualRect (&rcIntersect, &pSidebar->rcGadgetWindow [nGadget]))
                            {
                                Trace (_T ("LoadGadget: Gadget \"%s\" is loaded at (%d %d %d %d) and its HTMLHostWindow at (%d %d %d %d)\n"), pSidebar->szGadgetWindowTitle [nGadget], pSidebar->rcGadgetWindow [nGadget].left, pSidebar->rcGadgetWindow [nGadget].top, pSidebar->rcGadgetWindow [nGadget].right, pSidebar->rcGadgetWindow [nGadget].bottom, pSidebar->rcHTMLHostWindow [nGadget].left, pSidebar->rcHTMLHostWindow [nGadget].top, pSidebar->rcHTMLHostWindow [nGadget].right, pSidebar->rcHTMLHostWindow [nGadget].bottom);
                                nRetval = 1;
                            }
                            else
                            {
                                // Sometimes gadgets will load out of the screen space (e.g., when we have two displays with
                                // different heights and we move a full sidebar from the taller to the shorter).

                                Trace (_T ("LoadGadget: Gadget \"%s\" is loaded out of virtual screen at (%d %d %d %d) and its HTMLHostWindow at (%d %d %d %d)\n"), pSidebar->szGadgetWindowTitle [nGadget], pSidebar->rcGadgetWindow [nGadget].left, pSidebar->rcGadgetWindow [nGadget].top, pSidebar->rcGadgetWindow [nGadget].right, pSidebar->rcGadgetWindow [nGadget].bottom, pSidebar->rcHTMLHostWindow [nGadget].left, pSidebar->rcHTMLHostWindow [nGadget].top, pSidebar->rcHTMLHostWindow [nGadget].right, pSidebar->rcHTMLHostWindow [nGadget].bottom);
                                pSidebar->bIsOutOfScreen [nGadget]= TRUE;
                                nRetval = 2;
                            }
                        }
                        else
                        {
                            Trace (_T ("LoadGadget: Cannot get window rectangle of HTMLHostWindow of gadget \"%s\" (%d %d %d %d)\n"), pSidebar->szGadgetWindowTitle [nGadget], pSidebar->rcGadgetWindow [nGadget].left, pSidebar->rcGadgetWindow [nGadget].top, pSidebar->rcGadgetWindow [nGadget].right, pSidebar->rcGadgetWindow [nGadget].bottom);
                            nRetval = -5;
                        }
                    }
                    else
                    {
                        Trace (_T ("LoadGadget: Cannot get window rectangle of gadget \"%s\"\n"), pSidebar->szGadgetWindowTitle [nGadget]);
                        nRetval = -4;
                    }
                }
                else
                {
                    Trace (_T ("LoadGadget: Gadget \"%s\" is minimized\n"), pSidebar->szGadgetWindowTitle [nGadget]);
                    nRetval = -3;
                }
            }
            else
            {
                Trace (_T ("LoadGadget: Gadget \"%s\" is not visible\n"), pSidebar->szGadgetWindowTitle [nGadget]);
                nRetval = -2; // Must be -2 (see LoadGadgets).
            }
        }
        else
        {
            Trace (_T ("LoadGadget: Cannot get window text of gadget window 0x%08X\n"), pSidebar->hwndGadgetWindow [nGadget]);
            nRetval = -1;
        }
    }
    else
    {
        Trace (_T ("LoadGadget: Gadget window not found\n"));
        nRetval = 0;
    }

    return nRetval;
}

//////////////////////////////////////////////////////////////////////////////////////////
// > 0 - # of gadgets loaded.
//   0 - No gadgets loaded.
// < 0 - Error loading gadgets.
//       -1 and -2 indicate a high system load (we can adapt timeout).

INT LoadGadgets (SIDEBAR* pSidebar)
{
    INT nRetval;
    UINT nGadgetWindowNotVisible;
    UINT nGadget;

    (VOID) EnumWindows (FindGadgetWindowCallback, (LPARAM) pSidebar);

    if (pSidebar->bErrorLoadingGadgets)
    {
        return -1; // Typical situation is HTMLHostWindow not found.
    }

    nRetval = 0; nGadgetWindowNotVisible = 0;

    for (nGadget = 0; nGadget < pSidebar->nGadgets; nGadget++)
    {
        INT nResult = LoadGadget (pSidebar, nGadget);
        NOTRACE (_T ("LoadGadgets: LoadGadget returned %d\n"), nResult);

        if (nResult <= 0)
        {
            if (nResult == -2)
            {
                nGadgetWindowNotVisible++;
            }

            nRetval += (nGadget + 1) * -11 + nResult; // nResult between 0 and -10.
        }
    }

    if (nGadgetWindowNotVisible > 0 && nGadgetWindowNotVisible == pSidebar->nGadgets)
    {
        return -2; // None of the gadgets windows are visible.
    }

    return nRetval < 0 ? nRetval : pSidebar->nGadgets;
}

//////////////////////////////////////////////////////////////////////////////////////////
// > 0 - # of gadgets loaded.
//   0 - No gadgets loaded.
// < 0 - Error loading sidebar or gadgets.
//       -201 and -202 indicate a high system load (we can adapt timeout).

INT LoadCurrentSidebar (SIDEBAR* pSidebar)
{
    INT nRetval;

    Trace (_T ("LoadCurrentSidebar: Trying to load the sidebar...\n"));

    nRetval = LoadSidebar (pSidebar);
    TRACE (_T ("LoadCurrentSidebar: LoadSidebar returned %d\n"), nRetval);

    if (nRetval > 0)
    {
        nRetval = LoadGadgets (pSidebar);
        TRACE (_T ("LoadCurrentSidebar: LoadGadgets returned %d\n"), nRetval);

        if (nRetval < 0)
        {
            nRetval -= 200; // Gadgets load error below -200.
        }
    }
    else
    {
        nRetval -= 100; // Sidebar load error between -100 and -199.
    }

    return nRetval;
}

//////////////////////////////////////////////////////////////////////////////////////////

VOID SortGadgetsByCurrentPosition (SIDEBAR* pSidebar)
{
    UINT nOrder;
    UINT nCount;
    UINT nCurrent;
    UINT nNext;
    UINT nGadget;
    INT nCurrentTop;
    INT nNextTop;
    BOOL bSwap;

    for (nOrder = 0; nOrder < pSidebar->nGadgets; nOrder++)
    {
        pSidebar->nGadgetOrder [nOrder] = nOrder;
    }

    // pSidebar->nGadgetOrder will contain gadget index sorted by current top position.
    // Out of screen gadgets go to end of sort order.

    nCount = pSidebar->nGadgets;

    if (nCount > 1)
    {
        do
        {
            nCount = nCount - 1; bSwap = FALSE;

            for (nCurrent = 0; nCurrent < nCount; nCurrent = nNext)
            {
                nNext = nCurrent + 1;

                nGadget = pSidebar->nGadgetOrder [nCurrent];
                nCurrentTop = pSidebar->bIsOutOfScreen [nGadget] ? INT_MAX : pSidebar->rcGadgetWindow [nGadget].top;

                nGadget = pSidebar->nGadgetOrder [nNext];
                nNextTop = pSidebar->bIsOutOfScreen [nGadget] ? INT_MAX : pSidebar->rcGadgetWindow [nGadget].top;

                if (nCurrentTop > nNextTop)
                {
                    UINT nTemp = pSidebar->nGadgetOrder [nCurrent];
                    pSidebar->nGadgetOrder [nCurrent] = pSidebar->nGadgetOrder [nNext];
                    pSidebar->nGadgetOrder [nNext] = nTemp;
                    bSwap = TRUE;
                }
            }
        }
        while (bSwap);
    }

    TRACE (_T ("SortGadgetsByCurrentPosition\n"));

#ifdef _DEBUG

    for (nOrder = 0; nOrder < pSidebar->nGadgets; nOrder++)
    {
        UINT nGadget = pSidebar->nGadgetOrder [nOrder];

        TRACE (_T ("Order=%d title=[%s] position=(%d %d %d %d)%s\n"), nOrder,
                                                                      pSidebar->szGadgetWindowTitle [nGadget],
                                                                      pSidebar->rcGadgetWindow [nGadget].left,
                                                                      pSidebar->rcGadgetWindow [nGadget].top,
                                                                      pSidebar->rcGadgetWindow [nGadget].right,
                                                                      pSidebar->rcGadgetWindow [nGadget].bottom,
                                                                      pSidebar->bIsOutOfScreen [nGadget] ? _T (" <= out of screen") : _T (""));
    }

#endif
}

//////////////////////////////////////////////////////////////////////////////////////////

VOID SortGadgetsByOriginalPosition (SIDEBAR* pSidebar)
{
    UINT nOrder;
    UINT nCount;
    UINT nCurrent;
    UINT nNext;
    UINT nGadget;
    INT nCurrentTop;
    INT nNextTop;
    BOOL bSwap;

    for (nOrder = 0; nOrder < pSidebar->nGadgets; nOrder++)
    {
        pSidebar->nGadgetOrderOriginal [nOrder] = nOrder;
    }

    // pSidebar->nGadgetOrderOriginal will contain gadget index sorted by original top position.
    // Out of screen gadgets go to end of sort order.
    // Gadgets not found in profile go to end of sort order.

    nCount = pSidebar->nGadgets;

    if (nCount > 1)
    {
        do
        {
            nCount = nCount - 1; bSwap = FALSE;

            for (nCurrent = 0; nCurrent < nCount; nCurrent = nNext)
            {
                nNext = nCurrent + 1;

                // No need to test "bOriginalPositionNotFound" here, because if it happens "top" is already set to INT_MAX.

                nGadget = pSidebar->nGadgetOrderOriginal [nCurrent];
                nCurrentTop = pSidebar->bIsOutOfScreen [nGadget] ? INT_MAX : pSidebar->rcGadgetWindowOriginal [nGadget].top;
                    
                nGadget = pSidebar->nGadgetOrderOriginal [nNext];
                nNextTop = pSidebar->bIsOutOfScreen [nGadget] ? INT_MAX : pSidebar->rcGadgetWindowOriginal [nGadget].top;

                if (nCurrentTop > nNextTop)
                {
                    UINT nTemp = pSidebar->nGadgetOrderOriginal [nCurrent];
                    pSidebar->nGadgetOrderOriginal [nCurrent] = pSidebar->nGadgetOrderOriginal [nNext];
                    pSidebar->nGadgetOrderOriginal [nNext] = nTemp;
                    bSwap = TRUE;
                }
            }
        }
        while (bSwap);
    }

    TRACE (_T ("SortGadgetsByOriginalPosition\n"));

#ifdef _DEBUG

    for (nOrder = 0; nOrder < pSidebar->nGadgets; nOrder++)
    {
        UINT nGadget = pSidebar->nGadgetOrderOriginal [nOrder];

        if (pSidebar->bOriginalPositionNotFound [nGadget])
        {
            TRACE (_T ("Order=%d title=[%s] original position=(not found)%s\n"), nOrder,
                                                                                 pSidebar->szGadgetWindowTitle [nGadget],
                                                                                 pSidebar->bIsOutOfScreen [nGadget] ? _T (" <= current position is out of screen") : _T (""));
        }
        else
        {
            TRACE (_T ("Order=%d title=[%s] original position=(%d %d %d %d)%s\n"), nOrder,
                                                                                   pSidebar->szGadgetWindowTitle [nGadget],
                                                                                   pSidebar->rcGadgetWindowOriginal [nGadget].left,
                                                                                   pSidebar->rcGadgetWindowOriginal [nGadget].top,
                                                                                   pSidebar->rcGadgetWindowOriginal [nGadget].right,
                                                                                   pSidebar->rcGadgetWindowOriginal [nGadget].bottom,
                                                                                   pSidebar->bIsOutOfScreen [nGadget] ? _T (" <= current position is out of screen") : _T (""));
        }
    }

#endif
}

//////////////////////////////////////////////////////////////////////////////////////////

BOOL SaveGadgetPosition (SIDEBAR* pSidebar, UINT nGadget, LPCTSTR pszConfigFileName)
{
    INT nChars;
    TCHAR szBuffer [256];

    nChars = _sntprintf2 (szBuffer, _countof (szBuffer), _T ("%d %d %d %d"), pSidebar->rcGadgetWindow [nGadget].left,
                                                                             pSidebar->rcGadgetWindow [nGadget].top,
                                                                             pSidebar->rcGadgetWindow [nGadget].right,
                                                                             pSidebar->rcGadgetWindow [nGadget].bottom);
    if (nChars > 0)
    {
        if (Win32Call (WritePrivateProfileString (_T ("Gadgets"), pSidebar->szGadgetWindowTitle [nGadget], szBuffer, pszConfigFileName), _T ("SaveGadgetPosition.WritePrivateProfileString")))
        {
            Trace (_T ("SaveGadgetPosition: \"%s\" (%d %d %d %d)\n"), pSidebar->szGadgetWindowTitle [nGadget],
                                                                      pSidebar->rcGadgetWindow [nGadget].left,
                                                                      pSidebar->rcGadgetWindow [nGadget].top,
                                                                      pSidebar->rcGadgetWindow [nGadget].right,
                                                                      pSidebar->rcGadgetWindow [nGadget].bottom);
            return TRUE;
        }
    }

    return FALSE;
}

//////////////////////////////////////////////////////////////////////////////////////////

INT SaveGadgetsPosition (PARAM* pParam)
{
    UINT nGadget;
    SIDEBAR aSidebar;
    TCHAR szConfigFileName [MAX_PATH];

    UNREFERENCED_PARAMETER (pParam);

    if (LoadCurrentSidebar (&aSidebar) >= 0)
    {
        SortGadgetsByCurrentPosition (&aSidebar);

        GetConfigFileName (szConfigFileName, _countof (szConfigFileName));

        for (nGadget = 0; nGadget < aSidebar.nGadgets; nGadget++)
        {
            UINT nIndex = aSidebar.nGadgetOrder [nGadget];

            if (! aSidebar.bIsOutOfScreen [nIndex])
            {
                if (! SaveGadgetPosition (&aSidebar, nIndex, szConfigFileName))
                {
                    return pParam->bInstall ? 0 : 3;
                }
            }
            else
            {
                Trace (_T ("SaveGadgetsPosition: Ignoring gadget \"%s\" at position (%d %d %d %d) because it's out of screen space\n"), aSidebar.szGadgetWindowTitle [nIndex],
                    aSidebar.rcGadgetWindow [nIndex].left,
                    aSidebar.rcGadgetWindow [nIndex].top,
                    aSidebar.rcGadgetWindow [nIndex].right,
                    aSidebar.rcGadgetWindow [nIndex].bottom);
            }
        }
    }
    else
    {
        if (! pParam->bQuiet)
        {
            DisplayError (_T ("Sidebar window not found\n"));
        }

        return pParam->bInstall ? 0 : 3;
    }

    if (! pParam->bQuiet)
    {
        DisplayInformation (_T ("Gadgets position successfully saved.\n"));
    }

    return 0;
}

//////////////////////////////////////////////////////////////////////////////////////////

BOOL GadgetsStoodStill (SIDEBAR* pSidebar)
{
    SIDEBAR aSidebar;
    UINT nGadget;
    UINT nGadget2;
    BOOL bFound;

    Trace (_T ("GadgetsStoodStill: Checking if someone has moved...\n"));

    if (LoadCurrentSidebar (&aSidebar) < 0)
    {
        return FALSE;
    }

    if (! EqualRect (&aSidebar.rcSidebarWindow, &pSidebar->rcSidebarWindow))
    {
        Trace (_T ("GadgetsStoodStill: Sidebar \"%s\" moved from (%d %d %d %d) to (%d %d %d %d)\n"), pSidebar->szSidebarWindowTitle,
            pSidebar->rcSidebarWindow.left,
            pSidebar->rcSidebarWindow.top,
            pSidebar->rcSidebarWindow.right,
            pSidebar->rcSidebarWindow.bottom,
            aSidebar.rcSidebarWindow.left,
            aSidebar.rcSidebarWindow.top,
            aSidebar.rcSidebarWindow.right,
            aSidebar.rcSidebarWindow.bottom);

        return FALSE;
    }

    for (nGadget = 0; nGadget < pSidebar->nGadgets; nGadget++)
    {
        bFound = FALSE;

        for (nGadget2 = 0; nGadget2 < aSidebar.nGadgets; nGadget2++)
        {
            if (aSidebar.hwndGadgetWindow [nGadget2] == pSidebar->hwndGadgetWindow [nGadget])
            {
                if (EqualRect (&aSidebar.rcGadgetWindow [nGadget2], &pSidebar->rcGadgetWindow [nGadget]))
                {
                    if (EqualRect (&aSidebar.rcHTMLHostWindow [nGadget2], &pSidebar->rcHTMLHostWindow [nGadget]))
                    {
                        bFound = TRUE;
                        break;
                    }
                    else
                    {
                        Trace (_T ("GadgetsStoodStill: Gadget HTML Host Window\"%s\" moved from (%d %d %d %d) to (%d %d %d %d)\n"), pSidebar->szGadgetWindowTitle [nGadget],
                            pSidebar->rcHTMLHostWindow [nGadget].left,
                            pSidebar->rcHTMLHostWindow [nGadget].top,
                            pSidebar->rcHTMLHostWindow [nGadget].right,
                            pSidebar->rcHTMLHostWindow [nGadget].bottom,
                            aSidebar.rcHTMLHostWindow [nGadget2].left,
                            aSidebar.rcHTMLHostWindow [nGadget2].top,
                            aSidebar.rcHTMLHostWindow [nGadget2].right,
                            aSidebar.rcHTMLHostWindow [nGadget2].bottom);
                    }
                }
                else
                {
                    Trace (_T ("GadgetsStoodStill: Gadget \"%s\" moved from (%d %d %d %d) to (%d %d %d %d)\n"), pSidebar->szGadgetWindowTitle [nGadget],
                        pSidebar->rcGadgetWindow [nGadget].left,
                        pSidebar->rcGadgetWindow [nGadget].top,
                        pSidebar->rcGadgetWindow [nGadget].right,
                        pSidebar->rcGadgetWindow [nGadget].bottom,
                        aSidebar.rcGadgetWindow [nGadget2].left,
                        aSidebar.rcGadgetWindow [nGadget2].top,
                        aSidebar.rcGadgetWindow [nGadget2].right,
                        aSidebar.rcGadgetWindow [nGadget2].bottom);
                }

                return FALSE;
            }
        }

        if (! bFound)
        {
            Trace (_T ("GadgetsStoodStill: Gadget \"%s\" previously at (%d %d %d %d) was not found\n"), pSidebar->szGadgetWindowTitle [nGadget],
                pSidebar->rcGadgetWindow [nGadget].left,
                pSidebar->rcGadgetWindow [nGadget].top,
                pSidebar->rcGadgetWindow [nGadget].right,
                pSidebar->rcGadgetWindow [nGadget].bottom);

            return FALSE;
        }
    }

    for (nGadget2 = 0; nGadget2 < aSidebar.nGadgets; nGadget2++)
    {
        bFound = FALSE;

        for (nGadget = 0; nGadget < pSidebar->nGadgets; nGadget++)
        {
            if (pSidebar->hwndGadgetWindow [nGadget] == aSidebar.hwndGadgetWindow [nGadget2])
            {
                bFound = TRUE;
                break;
            }
        }

        if (! bFound)
        {
            Trace (_T ("GadgetsStoodStill: Gadget \"%s\" appeared at (%d %d %d %d)\n"), aSidebar.szGadgetWindowTitle [nGadget2],
                aSidebar.rcGadgetWindow [nGadget2].left,
                aSidebar.rcGadgetWindow [nGadget2].top,
                aSidebar.rcGadgetWindow [nGadget2].right,
                aSidebar.rcGadgetWindow [nGadget2].bottom);

            return FALSE;
        }
    }

    Trace (_T ("GadgetsStoodStill: Nobody has moved\n"));

    return TRUE;
}

//////////////////////////////////////////////////////////////////////////////////////////

VOID LoadOriginalGadgetsList (SIDEBAR* pSidebar, LPCTSTR pszConfigFileName)
{
    TCHAR szBuffer [SIDEBAR_MAX*128];

    pSidebar->nOriginalGadgets = 0;

    if (Win32Call (GetPrivateProfileString2 (_T ("Gadgets"), NULL, _T (""), szBuffer, _countof (szBuffer), pszConfigFileName), _T ("LoadOriginalGadgetsList.GetPrivateProfileString2")))
    {
        LPTSTR pszTitle;

        for (pszTitle = szBuffer; *pszTitle != _T ('\0'); pszTitle = pszTitle + _tcslen (pszTitle) + 1)
        {
            TRACE (_T ("LoadOriginalGadgetsList: Gadget \"%s\"\n"), pszTitle);

            if (pSidebar->nOriginalGadgets < _countof (pSidebar->szOriginalGadgetWindowTitle))
            {
                lstrcpyn (pSidebar->szOriginalGadgetWindowTitle [pSidebar->nOriginalGadgets], pszTitle, _countof (pSidebar->szOriginalGadgetWindowTitle [pSidebar->nOriginalGadgets]));

                pSidebar->nOriginalGadgets++;
            }
            else
            {
                Trace (_T ("LoadOriginalGadgetsList: Not enough space in array for gadget \"%s\"\n"), pszTitle);
            }
        }
    }
}

//////////////////////////////////////////////////////////////////////////////////////////

BOOL LoadGadgetOriginalPosition (SIDEBAR* pSidebar, UINT nGadget, LPCTSTR pszConfigFileName)
{
    RECT rcGadgetWindowOriginal;
    TCHAR szBuffer [256];

    if (Win32Call (GetPrivateProfileString2 (_T ("Gadgets"), pSidebar->szGadgetWindowTitle [nGadget], _T (""), szBuffer, _countof (szBuffer), pszConfigFileName), _T ("LoadGadgetOriginalPosition.GetPrivateProfileString2")))
    {
        if (szBuffer [0] != _T ('\0'))
        {
            if (_stscanf (szBuffer, _T ("%d %d %d %d"), &rcGadgetWindowOriginal.left, &rcGadgetWindowOriginal.top, &rcGadgetWindowOriginal.right, &rcGadgetWindowOriginal.bottom) == 4)
            {
                Trace (_T ("LoadGadgetOriginalPosition: Gadget \"%s\" original position is (%d %d %d %d)\n"), pSidebar->szGadgetWindowTitle [nGadget], rcGadgetWindowOriginal.left, rcGadgetWindowOriginal.top, rcGadgetWindowOriginal.right, rcGadgetWindowOriginal.bottom);
                pSidebar->rcGadgetWindowOriginal [nGadget] = rcGadgetWindowOriginal;
                return TRUE;
            }
            else
            {
                Trace (_T ("LoadGadgetOriginalPosition: Invalid configuration line in file %s for gadget %s\n"), pszConfigFileName, pSidebar->szGadgetWindowTitle [nGadget]);
            }
        }
        else
        {
            Trace (_T ("LoadGadgetOriginalPosition: Position not found in configuration file %s for gadget %s\n"), pszConfigFileName, pSidebar->szGadgetWindowTitle [nGadget]);
        }
    }
    else
    {
        Trace (_T ("LoadGadgetOriginalPosition: Position not found in configuration file %s for gadget %s\n"), pszConfigFileName, pSidebar->szGadgetWindowTitle [nGadget]);
    }

    (VOID) SetRect (&pSidebar->rcGadgetWindowOriginal [nGadget], INT_MAX, INT_MAX, INT_MAX, INT_MAX);
    pSidebar->bOriginalPositionNotFound [nGadget] = TRUE;
    return FALSE;
}

//////////////////////////////////////////////////////////////////////////////////////////

VOID LoadGadgetsOriginalPosition (SIDEBAR* pSidebar)
{
    UINT nGadget;
    TCHAR szConfigFileName [MAX_PATH];

    GetConfigFileName (szConfigFileName, _countof (szConfigFileName));

    LoadOriginalGadgetsList (pSidebar, szConfigFileName);

    for (nGadget = 0; nGadget < pSidebar->nGadgets; nGadget++)
    {
        (VOID) LoadGadgetOriginalPosition (pSidebar, nGadget, szConfigFileName);
    }
}

//////////////////////////////////////////////////////////////////////////////////////////

BOOL BringSidebarWindowToTop (SIDEBAR* pSidebar)
{
    BOOL bBrought;
    DWORD dwExStyle;
    UINT nGadget;

    bBrought = FALSE; dwExStyle = GetWindowLong (pSidebar->hwndSidebarWindow, GWL_EXSTYLE);

    if ((dwExStyle & WS_EX_TOPMOST) != 0)
    {
        TRACE (_T ("BringSidebarWindowToTop: 0x%08X WS_EX_TOPMOST ON\n"), pSidebar->hwndSidebarWindow);

        if (Win32Call (BringWindowToTop (pSidebar->hwndSidebarWindow), _T ("BringSidebarWindowToTop.BringWindowToTop")))
        {
            Trace (_T ("BringSidebarWindowToTop: Sidebar window was brought to top of Z order\n"));
            bBrought = TRUE;
        }
    }
    else
    {
        TRACE (_T ("BringSidebarWindowToTop: 0x%08X WS_EX_TOPMOST OFF\n"), pSidebar->hwndSidebarWindow);

        if (Win32Call (SetWindowPos (pSidebar->hwndSidebarWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOSIZE), _T ("BringSidebarWindowToTop.SetWindowPos")))
        {
            Trace (_T ("BringSidebarWindowToTop: Sidebar window was brought to top of Z order (topmost)\n"));
            bBrought = TRUE;
        }
    }

    if (! bBrought)
    {
        Trace (_T ("BringSidebarWindowToTop: Cannot bring sidebar window to top of Z order\n"));
        return FALSE;
    }

    for (nGadget = 0; nGadget < pSidebar->nGadgets; nGadget++)
    {
        if (! Win32Call (BringWindowToTop (pSidebar->hwndGadgetWindow [nGadget]), _T ("BringSidebarWindowToTop.BringWindowToTop")))
        {
            Trace (_T ("BringSidebarWindowToTop: Cannot bring gadget \"%s\" to top of Z order\n"), pSidebar->szGadgetWindowTitle [nGadget]);
            return FALSE;
        }

        if (! Win32Call (BringWindowToTop (pSidebar->hwndHTMLHostWindow [nGadget]), _T ("BringSidebarWindowToTop.BringWindowToTop")))
        {
            Trace (_T ("BringSidebarWindowToTop: Cannot bring HTMLHostWindow of gadget \"%s\" to top of Z order\n"), pSidebar->szGadgetWindowTitle [nGadget]);
            return FALSE;
        }

        if (! Win32Call (BringWindowToTop (pSidebar->hwndIEServerWindow [nGadget]), _T ("BringSidebarWindowToTop.BringWindowToTop")))
        {
            Trace (_T ("BringSidebarWindowToTop: Cannot bring IEServerWindow of gadget \"%s\" to top of Z order\n"), pSidebar->szGadgetWindowTitle [nGadget]);
            return FALSE;
        }
    }

    return TRUE;
}

//////////////////////////////////////////////////////////////////////////////////////////
//  1 - Gadget position restored.
//  0 - Gadget position not restored.
// -1 - Cursor was moved.

INT RestoreGadgetPosition1 (SIDEBAR* pSidebar, UINT nGadgetFrom, UINT nGadgetTo)
{
    INT x = pSidebar->rcGadgetWindow [nGadgetTo].left;
    INT y = pSidebar->rcGadgetWindow [nGadgetTo].top;
    INT cx = pSidebar->rcGadgetWindow [nGadgetTo].right - pSidebar->rcGadgetWindow [nGadgetTo].left;
    INT cy = pSidebar->rcGadgetWindow [nGadgetTo].bottom - pSidebar->rcGadgetWindow [nGadgetTo].top;

    return Win32Call (MoveWindow (pSidebar->hwndGadgetWindow [nGadgetFrom], x, y, cx, cy, TRUE), _T ("RestoreGadgetPosition1.MoveWindow")) ? 1 : 0;
}

//////////////////////////////////////////////////////////////////////////////////////////
//  1 - Gadget position restored.
//  0 - Gadget position not restored.
// -1 - Cursor was moved.

INT RestoreGadgetPosition2 (SIDEBAR* pSidebar, UINT nGadgetFrom, UINT nGadgetTo)
{
    INT x = pSidebar->rcGadgetWindow [nGadgetTo].left;
    INT y = pSidebar->rcGadgetWindow [nGadgetTo].top;

    return Win32Call (SetWindowPos (pSidebar->hwndGadgetWindow [nGadgetFrom], NULL, x, y, 0, 0, SWP_SHOWWINDOW|SWP_NOZORDER|SWP_NOSIZE), _T ("RestoreGadgetPosition2.SetWindowPos")) ? 1 : 0;
}

//////////////////////////////////////////////////////////////////////////////////////////

BOOL PtInSidebar (SIDEBAR* pSidebar, POINT ptTarget)
{
    HWND hwndTemp;

    if (IsButtonDown ())
    {
        return FALSE;
    }

    hwndTemp = WindowFromPoint (ptTarget);

#ifdef _DEBUG

    if (hwndTemp != NULL)
    {
        TCHAR szClass [256];
        TCHAR szTitle [256];

        (VOID) Win32Call (GetClassName2 (hwndTemp, szClass, _countof (szClass)), _T ("PtInSidebar.GetClassName2"));
        (VOID) Win32Call (GetWindowText2 (hwndTemp, szTitle, _countof (szTitle)), _T ("PtInSidebar.GetWindowText2"));

        Trace (_T ("PtInSidebar: Checking (%d %d) in sidebar \"%s\" - window 0x%08X \"%s\" %s\n"), ptTarget.x, ptTarget.y, pSidebar->szSidebarWindowTitle, hwndTemp, szTitle, szClass);
    }
    else
    {
        Trace (_T ("PtInSidebar: Checking (%d %d) in sidebar \"%s\" - no window exists at the given point\n"), ptTarget.x, ptTarget.y, pSidebar->szSidebarWindowTitle);
    }

#endif

    return hwndTemp == pSidebar->hwndSidebarWindow;
}

//////////////////////////////////////////////////////////////////////////////////////////
//  1 - Point is inside Sidebar.
//  0 - Timeout.
// -1 - Cursor was moved.

INT WaitForPointInsideSidebar (SIDEBAR* pSidebar, POINT ptCursor, DWORD dwTimeout, LPCTSTR pszWaitingFor)
{
    INT nRetval = 1;
    DWORD dwStartTime;
    POINT ptCursor1;
    POINT ptCursor2;

    SaveCursorPos (&ptCursor1);

    Trace (_T ("WaitForPointInsideSidebar: Waiting (%s) point (%d %d) inside sidebar \"%s\"\n"), pszWaitingFor, ptCursor.x, ptCursor.y, pSidebar->szSidebarWindowTitle);

    dwStartTime = GetTickCount ();

    while (! PtInSidebar (pSidebar, ptCursor))
    {
        if (GetTickCount () - dwStartTime >= dwTimeout)
        {
            nRetval = 0;
            break;
        }

        Sleep (25);
    }

    SaveCursorPos (&ptCursor2);

    return EqualPoint (ptCursor1, ptCursor2) ? nRetval : -1;
}

//////////////////////////////////////////////////////////////////////////////////////////

BOOL PtInGadget (SIDEBAR* pSidebar, UINT nGadget, POINT ptTarget)
{
    HWND hwndTemp;

    if (IsButtonDown ())
    {
        return FALSE;
    }

    hwndTemp = WindowFromPoint (ptTarget);

#ifdef _DEBUG

    if (hwndTemp != NULL)
    {
        TCHAR szClass [256];
        TCHAR szTitle [256];

        (VOID) Win32Call (GetClassName2 (hwndTemp, szClass, _countof (szClass)), _T ("PtInGadget.GetClassName2"));
        (VOID) Win32Call (GetWindowText2 (hwndTemp, szTitle, _countof (szTitle)), _T ("PtInGadget.GetWindowText2"));

        Trace (_T ("PtInGadget: Checking (%d %d) in gadget \"%s\" - window 0x%08X \"%s\" %s\n"), ptTarget.x, ptTarget.y, pSidebar->szGadgetWindowTitle [nGadget], hwndTemp, szTitle, szClass);
    }
    else
    {
        Trace (_T ("PtInGadget: Checking (%d %d) in gadget \"%s\" - no window exists at the given point\n"), ptTarget.x, ptTarget.y, pSidebar->szGadgetWindowTitle [nGadget]);
    }

#endif

    return hwndTemp == pSidebar->hwndGadgetWindow [nGadget] || hwndTemp == pSidebar->hwndHTMLHostWindow [nGadget] || hwndTemp == pSidebar->hwndIEServerWindow [nGadget];
}

//////////////////////////////////////////////////////////////////////////////////////////
//  1 - Point is inside Sidebar.
//  0 - Timeout.
// -1 - Cursor was moved.

BOOL WaitForPointInsideGadget (SIDEBAR* pSidebar, UINT nGadget, POINT ptCursor, DWORD dwTimeout, LPCTSTR pszWaitingFor)
{
    INT nRetval = 1;
    DWORD dwStartTime;
    POINT ptCursor1;
    POINT ptCursor2;

    SaveCursorPos (&ptCursor1);

    Trace (_T ("WaitForPointInsideGadget: Waiting (%s) point (%d %d) inside gadget \"%s\"\n"), pszWaitingFor, ptCursor.x, ptCursor.y, pSidebar->szGadgetWindowTitle [nGadget]);

    dwStartTime = GetTickCount ();

    while (! PtInGadget (pSidebar, nGadget, ptCursor))
    {
        if (GetTickCount () - dwStartTime >= dwTimeout)
        {
            nRetval = 0;
            break;
        }

        Sleep (25);
    }

    SaveCursorPos (&ptCursor2);

    return EqualPoint (ptCursor1, ptCursor2) ? nRetval : -1;
}

//////////////////////////////////////////////////////////////////////////////////////////
//  1 - OK.
//  0 - Timeout.
// -1 - Cursor was moved.

INT ClickInsideSidebar (SIDEBAR* pSidebar, INT cxScreen, INT cyScreen, DWORD dwTimeout)
{
    INT nRetval;
    POINT ptCursor;

    ptCursor.x = (pSidebar->rcSidebarWindow.left + pSidebar->rcSidebarWindow.right) / 2;
    ptCursor.y = pSidebar->rcSidebarWindow.top + 1;

    if ((nRetval = WaitForPointInsideSidebar (pSidebar, ptCursor, dwTimeout, _T ("point to click"))) <= 0)
    {
        return nRetval;
    }

    TRACE (_T ("ClickInsideSidebar: Clicking point (%d %d)\n"), ptCursor.x, ptCursor.y);

    mouse_event (MOUSEEVENTF_MOVE|MOUSEEVENTF_ABSOLUTE, (DWORD) (ptCursor.x * 65535 / cxScreen), (DWORD) (ptCursor.y * 65535 / cyScreen), 0, 0);
    mouse_event (MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);

    Sleep (5);

    mouse_event (MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);

    Sleep (5);

    return nRetval;
}

//////////////////////////////////////////////////////////////////////////////////////////
//  1 - Gadget position restored.
//  0 - Gadget position not restored.
// -1 - Cursor was moved.

INT RestoreGadgetPosition3 (SIDEBAR* pSidebar, UINT nGadgetFrom, UINT nGadgetTo)
{
    INT nGadgetWasMoved = 0;
    RECT rcPrimaryScreen;
    INT nResult;
    INT cxScreen;
    INT cyScreen;
    POINT ptCursor;
    RECT rcGadgetWindow;

    GetPrimaryScreenRect (&rcPrimaryScreen);

    cxScreen = rcPrimaryScreen.right - rcPrimaryScreen.left;
    cyScreen = rcPrimaryScreen.bottom - rcPrimaryScreen.top;

    if (! BringSidebarWindowToTop (pSidebar))
    {
        return FALSE;
    }

    ptCursor.x = (pSidebar->rcGadgetWindow [nGadgetFrom].left + pSidebar->rcGadgetWindow [nGadgetFrom].right) / 2;
    ptCursor.y = (pSidebar->rcGadgetWindow [nGadgetFrom].top + pSidebar->rcGadgetWindow [nGadgetFrom].bottom) / 2;

    nResult = WaitForPointInsideGadget (pSidebar, nGadgetFrom, ptCursor, 500, _T ("gadget center point"));

    if (nResult > 0) // Point is inside gadget.
    {
        mouse_event (MOUSEEVENTF_MOVE|MOUSEEVENTF_ABSOLUTE, (DWORD) (ptCursor.x * 65535 / cxScreen), (DWORD) (ptCursor.y * 65535 / cyScreen), 0, 0);

        Sleep (5);

        if (pSidebar->nSidebarDockSide == SIDEBAR_DOCKSIDE_LEFT)
        {
            ptCursor.x = (pSidebar->rcHTMLHostWindow [nGadgetFrom].left + pSidebar->rcGadgetWindow [nGadgetFrom].left) / 2;
        }
        else
        {
            if (pSidebar->nSidebarDockSide == SIDEBAR_DOCKSIDE_RIGHT)
            {
                ptCursor.x = (pSidebar->rcHTMLHostWindow [nGadgetFrom].right + pSidebar->rcGadgetWindow [nGadgetFrom].right) / 2;
            }
            else
            {
                Trace (_T ("RestoreGadgetPosition3: Invalid sidebar dock side: %d\n"), pSidebar->nSidebarDockSide);
                ptCursor.x = 0;
            }
        }

        ptCursor.y = pSidebar->rcHTMLHostWindow [nGadgetFrom].top;

        nResult = WaitForPointInsideGadget (pSidebar, nGadgetFrom, ptCursor, 500, _T ("dragging hotspot"));

        if (nResult > 0) // Point is inside gadget.
        {
            ptCursor.y = pSidebar->rcHTMLHostWindow [nGadgetFrom].bottom - 1;

            Trace (_T ("Searching for dragging hotspot from (%d %d) to (%d %d)\n"), ptCursor.x, ptCursor.y, ptCursor.x, pSidebar->rcHTMLHostWindow [nGadgetFrom].top);

            while (ptCursor.x != 0 && ptCursor.y >= pSidebar->rcHTMLHostWindow [nGadgetFrom].top)
            {
                if (PtInGadget (pSidebar, nGadgetFrom, ptCursor))
                {
                    POINT ptDest;
                    RECT rcDest;

                    mouse_event (MOUSEEVENTF_MOVE|MOUSEEVENTF_ABSOLUTE, (DWORD) (ptCursor.x * 65535 / cxScreen), (DWORD) (ptCursor.y * 65535 / cyScreen), 0, 0);
                    mouse_event (MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);

                    Sleep (5);

                    ptDest.x = ptCursor.x;
                    ptDest.y = pSidebar->rcHTMLHostWindow [nGadgetTo].top + ((pSidebar->rcHTMLHostWindow [nGadgetFrom].top + pSidebar->rcHTMLHostWindow [nGadgetFrom].bottom) / 2 - pSidebar->rcHTMLHostWindow [nGadgetFrom].top);

                    mouse_event (MOUSEEVENTF_MOVE|MOUSEEVENTF_ABSOLUTE, (DWORD) (ptDest.x * 65535 / cxScreen), (DWORD) (ptDest.y * 65535 / cyScreen), 0, 0);
                    mouse_event (MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);

                    Sleep (10);

                    if (Win32Call (GetWindowRect (pSidebar->hwndGadgetWindow [nGadgetFrom], &rcDest), _T ("RestoreGadgetPosition3.GetWindowRect")))
                    {
                        if (rcDest.top != pSidebar->rcGadgetWindow [nGadgetFrom].top) // If gadget moves...
                        {
                            if (Win32Call (GetWindowRect (pSidebar->hwndGadgetWindow [nGadgetFrom], &rcGadgetWindow), _T ("RestoreGadgetPosition3.GetWindowRect")))
                            {
                                Trace (_T ("RestoreGadgetPosition3: Successfully dragged gadget \"%s\" to (%d %d %d %d)\n"), pSidebar->szGadgetWindowTitle [nGadgetFrom], rcGadgetWindow.left, rcGadgetWindow.top, rcGadgetWindow.right, rcGadgetWindow.bottom);
                            }

                            nGadgetWasMoved = 1;
                            break;
                        }
                    }
                }

                ptCursor.y -= 2;
            }
        }
        else
        {
            // nResult == 0 => Timeout waiting for point inside gadget, nResult < 0 => Cursor movement.

            nGadgetWasMoved = nResult;

            if (Win32Call (GetWindowRect (pSidebar->hwndGadgetWindow [nGadgetFrom], &rcGadgetWindow), _T ("RestoreGadgetPosition3.GetWindowRect")))
            {
                if (EqualRect (&rcGadgetWindow, &pSidebar->rcGadgetWindow [nGadgetFrom]))
                {
                    Trace (_T ("RestoreGadgetPosition3: %s waiting for dragging hotspot (%d %d) of gadget \"%s\" (%d %d %d %d)\n"), nResult < 0 ? _T ("Cursor movement") : _T ("Timeout"), ptCursor.x, ptCursor.y, pSidebar->szGadgetWindowTitle [nGadgetFrom], rcGadgetWindow.left, rcGadgetWindow.top, rcGadgetWindow.right, rcGadgetWindow.bottom);
                }
                else
                {
                    Trace (_T ("RestoreGadgetPosition3: %s waiting for dragging hotspot (%d %d) of gadget \"%s\" (%d %d %d %d) NOW AT (%d %d %d %d)\n"), nResult < 0 ? _T ("Cursor movement") : _T ("Timeout"), ptCursor.x, ptCursor.y, pSidebar->szGadgetWindowTitle [nGadgetFrom], pSidebar->rcGadgetWindow [nGadgetFrom].left, pSidebar->rcGadgetWindow [nGadgetFrom].top, pSidebar->rcGadgetWindow [nGadgetFrom].right, pSidebar->rcGadgetWindow [nGadgetFrom].bottom, rcGadgetWindow.left, rcGadgetWindow.top, rcGadgetWindow.right, rcGadgetWindow.bottom);
                }
            }
        }
    }
    else
    {
        // nResult == 0 => Timeout waiting for point inside gadget, nResult < 0 => Cursor movement.

        nGadgetWasMoved = nResult;

        if (Win32Call (GetWindowRect (pSidebar->hwndGadgetWindow [nGadgetFrom], &rcGadgetWindow), _T ("RestoreGadgetPosition3.GetWindowRect")))
        {
            if (EqualRect (&rcGadgetWindow, &pSidebar->rcGadgetWindow [nGadgetFrom]))
            {
                Trace (_T ("RestoreGadgetPosition3: %s waiting for center point (%d %d) of gadget \"%s\" (%d %d %d %d)\n"), nResult < 0 ? _T ("Cursor movement") : _T ("Timeout"), ptCursor.x, ptCursor.y, pSidebar->szGadgetWindowTitle [nGadgetFrom], rcGadgetWindow.left, rcGadgetWindow.top, rcGadgetWindow.right, rcGadgetWindow.bottom);
            }
            else
            {
                Trace (_T ("RestoreGadgetPosition3: %s waiting for center point (%d %d) of gadget \"%s\" (%d %d %d %d) NOW AT (%d %d %d %d)\n"), nResult < 0 ? _T ("Cursor movement") : _T ("Timeout"), ptCursor.x, ptCursor.y, pSidebar->szGadgetWindowTitle [nGadgetFrom], pSidebar->rcGadgetWindow [nGadgetFrom].left, pSidebar->rcGadgetWindow [nGadgetFrom].top, pSidebar->rcGadgetWindow [nGadgetFrom].right, pSidebar->rcGadgetWindow [nGadgetFrom].bottom, rcGadgetWindow.left, rcGadgetWindow.top, rcGadgetWindow.right, rcGadgetWindow.bottom);
            }
        }
    }

    if (nGadgetWasMoved >= 0)
    {
        (VOID) ClickInsideSidebar (pSidebar, cxScreen, cyScreen, 250);
    }

    return nGadgetWasMoved;
}

//////////////////////////////////////////////////////////////////////////////////////////
//  1 - Gadget position restored.
//  0 - Gadget position not restored.
// -1 - Cursor was moved.

INT RestoreGadgetPosition (UINT nMethod, SIDEBAR* pSidebar, UINT nGadgetFrom, UINT nGadgetTo)
{
    INT nRetval;
    POINT ptCursor;

    INT nWidth = pSidebar->rcGadgetWindow [nGadgetFrom].right - pSidebar->rcGadgetWindow [nGadgetFrom].left;
    INT nHeight = pSidebar->rcGadgetWindow [nGadgetFrom].bottom - pSidebar->rcGadgetWindow [nGadgetFrom].top;

    if (pSidebar->bIsOutOfScreen [nGadgetFrom])
    {
        Trace (_T ("RestoreGadgetPosition: Ignoring move of gadget \"%s\" from (%d %d %d %d) to (%d %d %d %d) because it's out of screen space\n"), pSidebar->szGadgetWindowTitle [nGadgetFrom],
            pSidebar->rcGadgetWindow [nGadgetFrom].left,
            pSidebar->rcGadgetWindow [nGadgetFrom].top,
            pSidebar->rcGadgetWindow [nGadgetFrom].right,
            pSidebar->rcGadgetWindow [nGadgetFrom].bottom,
            pSidebar->rcGadgetWindow [nGadgetTo].left,
            pSidebar->rcGadgetWindow [nGadgetTo].top,
            pSidebar->rcGadgetWindow [nGadgetTo].left + nWidth,
            pSidebar->rcGadgetWindow [nGadgetTo].top + nHeight);

        return 0;
    }

    Trace (_T ("RestoreGadgetPosition: Moving gadget \"%s\" from (%d %d %d %d) to (%d %d %d %d)\n"), pSidebar->szGadgetWindowTitle [nGadgetFrom],
        pSidebar->rcGadgetWindow [nGadgetFrom].left,
        pSidebar->rcGadgetWindow [nGadgetFrom].top,
        pSidebar->rcGadgetWindow [nGadgetFrom].right,
        pSidebar->rcGadgetWindow [nGadgetFrom].bottom,
        pSidebar->rcGadgetWindow [nGadgetTo].left,
        pSidebar->rcGadgetWindow [nGadgetTo].top,
        pSidebar->rcGadgetWindow [nGadgetTo].left + nWidth,
        pSidebar->rcGadgetWindow [nGadgetTo].top + nHeight);

    SaveCursorPos (&ptCursor);

    switch (nMethod)
    {
        case 1: nRetval = RestoreGadgetPosition1 (pSidebar, nGadgetFrom, nGadgetTo); break;
        case 2: nRetval = RestoreGadgetPosition2 (pSidebar, nGadgetFrom, nGadgetTo); break;
        case 3: nRetval = RestoreGadgetPosition3 (pSidebar, nGadgetFrom, nGadgetTo); break;

        default:
        {
            Trace (_T ("RestoreGadgetPosition: Unrecognized method: %u\n"), nMethod);
            nRetval = 0;
        }
    }

    if (nRetval >= 0)
    {
        RestoreCursorPos (&ptCursor);
    }

    return nRetval;
}

//////////////////////////////////////////////////////////////////////////////////////////

UINT FindFirstUnorderedGadget (SIDEBAR* pSidebar, UINT* pnGadgetTo)
{
    UINT nIndex;

    for (nIndex = 0; nIndex < pSidebar->nGadgets; nIndex++)
    {
        UINT nGadget = pSidebar->nGadgetOrderOriginal [nIndex];

        if (pSidebar->bIsOutOfScreen [nGadget] || pSidebar->bOriginalPositionNotFound [nGadget])
        {
            break;
        }

        if (nGadget != pSidebar->nGadgetOrder [nIndex])
        {
            *pnGadgetTo = pSidebar->nGadgetOrder [nIndex];
            return nGadget;
        }
    }

    return NO_GADGET;
}

//////////////////////////////////////////////////////////////////////////////////////////

BOOL IsThereAnyGadgetLoadedOutOfScreen (SIDEBAR* pSidebar)
{
    UINT nGadget;

    for (nGadget = 0; nGadget < pSidebar->nGadgets; nGadget++)
    {
        if (pSidebar->bIsOutOfScreen [nGadget])
        {
            return TRUE;
        }
    }

    return FALSE;
}

//////////////////////////////////////////////////////////////////////////////////////////
// No need to test "bIsOutOfScreen" here, because this function is called only when
// IsThereAnyGadgetLoadedOutOfScreen returns FALSE.

BOOL AllOriginalGadgetsAreRestored (SIDEBAR* pSidebar)
{
    BOOL bFound = TRUE;
    UINT nOriginalGadget;

    for (nOriginalGadget = 0; nOriginalGadget < pSidebar->nOriginalGadgets && bFound; nOriginalGadget++)
    {
        UINT nGadget;

        for (bFound = FALSE, nGadget = 0; nGadget < pSidebar->nGadgets; nGadget++)
        {
            if (_tcscmp (pSidebar->szGadgetWindowTitle [nGadget], pSidebar->szOriginalGadgetWindowTitle [nOriginalGadget]) == 0)
            {
                bFound = TRUE; // We assume bIsOutOfScreen is FALSE.
                break;
            }
        }
    }

    return bFound;
}

//////////////////////////////////////////////////////////////////////////////////////////
// This function uses adaptive timeout handling: if the user moves the cursor or if the
// function detects that things are evolving, the timer  is restarted. A timeout can
// occur only if the sidebar state remains the same during the timeout period and also
// if the user does not move the cursor during the same period. Note: cursor movement is
// checked only if bRestartTimerIfCursorMoves is TRUE.

INT RestoreGadgetsPosition (PARAM* pParam)
{
    INT nExitCode = 3;
    INT nStatusOld = -1;
    DWORD dwWaitTime = 250;
    DWORD dwStartTime;
    DWORD dwTimeLimit;
    DWORD dwExtraTime;
    DWORD dwMaxExtraTime;
    SIDEBAR aSidebar;
    INT nResetCount;
    INT nRestoreCount;

    Trace (_T ("RestoreGadgetsPosition: Timeout = %u\n"), pParam->nTimeout);

    dwTimeLimit = pParam->nTimeout * 1000;

    if ((dwMaxExtraTime = dwTimeLimit / 5) > 5000)
    {
        dwMaxExtraTime = 5000;
    }

    dwStartTime = GetTickCount ();
    dwExtraTime = 0;
    nResetCount = 0;
    nRestoreCount = 0;

    while (GetTickCount () - dwStartTime < dwTimeLimit)
    {
        INT nStatus = LoadCurrentSidebar (&aSidebar);

        TRACE (_T ("RestoreGadgetsPosition: LoadCurrentSidebar returned %d\n"), nStatus);

        if (nStatus != nStatusOld)
        {
            Trace (_T ("RestoreGadgetsPosition: Changing status from %d to %d (restarting timer)\n"), nStatusOld, nStatus);
            nStatusOld = nStatus;
            dwStartTime = GetTickCount ();
            dwExtraTime = 0;
            nResetCount = 0;
        }
        else
        {
            if (nStatus == -201 || nStatus == -202)
            {
                // These codes are typical indication of a high system load.

                if (GetTickCount () - dwStartTime >= dwTimeLimit - dwMaxExtraTime)
                {
                    if (++nResetCount <= 3)
                    {
                        Trace (_T ("RestoreGadgetsPosition: System load seems to be high (restarting timer)\n"));
                        dwStartTime = GetTickCount ();
                        dwExtraTime += dwMaxExtraTime / 5;
                    }
                }
            }
        }

        if (nStatus >= 0) // nStatus is # of gadgets loaded if >= 0 or error indicator if < 0.
        {
            INT nLoadedGadgets = nStatus;

            if (MySleep (dwWaitTime, pParam->bRestartTimerIfCursorMoves))
            {
                if (GadgetsStoodStill (&aSidebar))
                {
                    UINT nGadgetFrom;
                    UINT nGadgetTo;

                    LoadGadgetsOriginalPosition (&aSidebar);

                    SortGadgetsByOriginalPosition (&aSidebar);
                    SortGadgetsByCurrentPosition (&aSidebar);

                    if ((nGadgetFrom = FindFirstUnorderedGadget (&aSidebar, &nGadgetTo)) == NO_GADGET)
                    {
                        nExitCode = 0; dwExtraTime += dwMaxExtraTime / 10;

                        // A gadget loaded out of virtual screen can enters the screen and change the order of the
                        // already loaded gadgets.

                        if (IsThereAnyGadgetLoadedOutOfScreen (&aSidebar))
                        {
                            Trace (_T ("RestoreGadgetsPosition: All loaded gadgets were restored to their original positions, but there are gadgets out of screen and we must wait for them\n"));
                        }
                        else
                        {
                            if (AllOriginalGadgetsAreRestored (&aSidebar))
                            {
                                Trace (_T ("RestoreGadgetsPosition: All original gadgets are loaded and were restored to their original positions\n"));
                                break;
                            }

                            if (aSidebar.nGadgets > 0)
                            {
                                Trace (_T ("RestoreGadgetsPosition: All loaded gadgets were restored to their original positions\n"));
                            }
                            else
                            {
                                Trace (_T ("RestoreGadgetsPosition: No gadgets were found\n"));
                            }
                        }
                    }
                    else
                    {
                        INT nResult = RestoreGadgetPosition (pParam->nMethod, &aSidebar, nGadgetFrom, nGadgetTo);

                        if (nResult > 0)
                        {
                            if (++nRestoreCount <= nLoadedGadgets) // avoids infinite loop if we do a fake move
                            {
                                Trace (_T ("RestoreGadgetsPosition: Restored gadget \"%s\" to its original position (restarting timer)\n"), aSidebar.szGadgetWindowTitle [nGadgetFrom]);
                                dwStartTime = GetTickCount ();
                                dwExtraTime = 0;
                            }
                            else
                            {
                                Trace (_T ("RestoreGadgetsPosition: Restored gadget \"%s\" to its original position\n"), aSidebar.szGadgetWindowTitle [nGadgetFrom]);
                                dwExtraTime = 0;
                            }
                        }
                        else if (nResult == 0)
                        {
                            Trace (_T ("RestoreGadgetsPosition: Cannot restore gadget \"%s\" to its original position\n"), aSidebar.szGadgetWindowTitle [nGadgetFrom]);
                            dwExtraTime += dwMaxExtraTime / 5;
                        }
                        else
                        {
                            if (pParam->bRestartTimerIfCursorMoves)
                            {
                                Trace (_T ("RestoreGadgetsPosition: Cannot restore gadget \"%s\" to its original position due to cursor movement (restarting timer)\n"), aSidebar.szGadgetWindowTitle [nGadgetFrom]);
                                dwStartTime = GetTickCount ();
                                dwExtraTime += dwMaxExtraTime / 5;
                            }
                            else
                            {
                                Trace (_T ("RestoreGadgetsPosition: Cannot restore gadget \"%s\" to its original position due to cursor movement\n"), aSidebar.szGadgetWindowTitle [nGadgetFrom]);
                            }
                        }

                        nExitCode = 3;
                    }
                }
                else
                {
                    Trace (_T ("RestoreGadgetsPosition: Someone has moved (restarting timer)\n"));
                    dwStartTime = GetTickCount ();
                    dwExtraTime = 0;
                    nExitCode = 3;
                }
            }
            else
            {
                // When we reach final exit code, we must respect the timeout period, otherwise
                // we can keep waiting forever while the user works.

                if (nExitCode == 0)
                {
                    Trace (_T ("RestoreGadgetsPosition: Cursor movement ignored (exit code zero) - Timer still counting\n"));
                }
                else
                {
                    Trace (_T ("RestoreGadgetsPosition: Cursor movement detected (restarting timer)\n"));
                    dwStartTime = GetTickCount ();
                    dwExtraTime += dwMaxExtraTime / 5;
                }
            }
        }
        else
        {
            nExitCode = 3; // do not change dwExtraTime here.
        }

        if (dwExtraTime > dwMaxExtraTime)
        {
            dwExtraTime = dwMaxExtraTime;
        }

        if (! MySleep (dwWaitTime + dwExtraTime, pParam->bRestartTimerIfCursorMoves))
        {
            // When we reach final exit code, we must respect the timeout period, otherwise
            // we can keep waiting forever while the user works.

            if (nExitCode == 0)
            {
                Trace (_T ("RestoreGadgetsPosition: Cursor movement ignored (exit code zero) - Timer still counting\n"));
            }
            else
            {
                Trace (_T ("RestoreGadgetsPosition: Cursor movement detected (restarting timer)\n"));
                dwStartTime = GetTickCount ();
                dwExtraTime += dwMaxExtraTime / 5;
            }
        }
    }

    Trace (_T ("RestoreGadgetsPosition: Exiting with status %d\n"), nExitCode);

    return nExitCode;
}

//////////////////////////////////////////////////////////////////////////////////////////

INT nWindowTreeLevel = 0;

VOID ShowSidebarWindowTree (HWND hWnd);

//////////////////////////////////////////////////////////////////////////////////////////

BOOL CALLBACK ShowSidebarWindowTreeCallback (HWND hWnd, LPARAM lParam)
{
    HWND hwndTarget = (HWND) lParam;
    HWND hwndParent = (HWND) (LONG_PTR) GetWindowLong (hWnd, GWL_HWNDPARENT);

    if (hwndParent == hwndTarget)
    {
        ShowSidebarWindowTree (hWnd);
    }

    return TRUE;
}

//////////////////////////////////////////////////////////////////////////////////////////

BOOL CALLBACK ShowSidebarWindowTreeChildCallback (HWND hwndChild, LPARAM lParam)
{
    UNREFERENCED_PARAMETER (lParam);

    ShowSidebarWindowTree (hwndChild);

    return TRUE;
}

//////////////////////////////////////////////////////////////////////////////////////////

VOID ShowSidebarWindowTree (HWND hWnd)
{
    DWORD dwStyle;
    DWORD dwExStyle;
    TCHAR szClassName [256];
    TCHAR szTitle [256];
    RECT rcWindow;
    HDC hdc;
    HBRUSH hbr;

    dwStyle = GetWindowLong (hWnd, GWL_STYLE);
    dwExStyle = GetWindowLong (hWnd, GWL_EXSTYLE);

    if (Win32Call (GetClassName2 (hWnd, szClassName, _countof (szClassName)), _T ("ShowSidebarWindowTree.GetClassName2")))
    {
        if (Win32Call (GetWindowText2 (hWnd, szTitle, _countof (szTitle)), _T ("ShowSidebarWindowTree.GetWindowText2")))
        {
            if (Win32Call (GetWindowRect (hWnd, &rcWindow), _T ("ShowSidebarWindowTree.GetWindowRect")))
            {
                Trace (_T ("%*s0x%08X [0x%08X] \"%s\" \"%s\" %d %d %d %d%s%s%s%s%s\n"), nWindowTreeLevel, _T (""),
                    hWnd, dwStyle, szClassName, szTitle, rcWindow.left, rcWindow.top, rcWindow.right, rcWindow.bottom,
                    (dwStyle & (WS_CHILD|WS_POPUP)) == 0 ? _T (" WS_OVERLAPPED") : _T (""),
                    (dwStyle & WS_CHILD) != 0 ? _T (" WS_CHILD") : _T (""),
                    (dwStyle & WS_POPUP) != 0 ? _T (" WS_POPUP") : _T (""),
                    (dwStyle & WS_VISIBLE) != 0 ? _T (" WS_VISIBLE") : _T (""),
                    (dwExStyle & WS_EX_TOPMOST) != 0 ? _T (" WS_EX_TOPMOST") : _T (""));

                if ((dwStyle & WS_VISIBLE) != 0)
                {
                    hdc = GetDC (NULL);

                    if (Win32Call (hdc != NULL, _T ("ShowSidebarWindowTree.GetDC")))
                    {
                        hbr = GetStockBrush (WHITE_BRUSH);

                        if (Win32Call (hbr != NULL, _T ("ShowSidebarWindowTree.GetStockBrush")))
                        {
                            (VOID) Win32Call (FrameRect (hdc, &rcWindow, hbr), _T ("ShowSidebarWindowTree.FrameRect"));
                        }

                        (VOID) ReleaseDC (NULL, hdc);
                    }
                }

                // Walk windows we are parent of.

                nWindowTreeLevel++;

                (VOID) EnumWindows (ShowSidebarWindowTreeCallback, (LPARAM) hWnd);

                (VOID) EnumChildWindows (hWnd, ShowSidebarWindowTreeChildCallback, 0);

                --nWindowTreeLevel;
            }
        }
    }
}

//////////////////////////////////////////////////////////////////////////////////////////

INT ShowSidebarWindows (PARAM* pParam)
{
    SIDEBAR aSidebar;

    UNREFERENCED_PARAMETER (pParam);

    ZeroMemory (&aSidebar, sizeof (SIDEBAR));

    (VOID) EnumWindows (FindSidebarWindowCallback, (LPARAM) &aSidebar);

    if (aSidebar.hwndSidebarWindow != NULL)
    {
        ShowSidebarWindowTree (aSidebar.hwndSidebarWindow);
    }
    else
    {
        if (! pParam->bQuiet)
        {
            DisplayError (_T ("Sidebar window not found\n"));
        }
    }

    return 0;
}

//////////////////////////////////////////////////////////////////////////////////////////

INT ArrangeVistaSidebar (PARAM* pParam)
{
    if (pParam->bSaveGadgetsPosition)
    {
        return SaveGadgetsPosition (pParam);
    }

    if (pParam->bRestoreGadgetsPosition)
    {
        return RestoreGadgetsPosition (pParam);
    }

    if (pParam->bShowWindows)
    {
        return ShowSidebarWindows (pParam);
    }

    return DisplayHelp (), 3;
}

//////////////////////////////////////////////////////////////////////////////////////////

VOID Terminate (PARAM* pParam)
{
    UNREFERENCED_PARAMETER (pParam);
}

//////////////////////////////////////////////////////////////////////////////////////////

INT APIENTRY _tWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR pszCmdLine, INT nCmdShow)
{
    INT nStatus;
    PARAM aParam;

    UNREFERENCED_PARAMETER (hPrevInstance);
    UNREFERENCED_PARAMETER (nCmdShow);

    nStatus = Initialize (hInstance, pszCmdLine, &aParam);

    if (nStatus == 0)
    {
        nStatus = ArrangeVistaSidebar (&aParam);
    }

    Terminate (&aParam);

    return nStatus;
}
